using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using CatLib.Container;
using Cysharp.Threading.Tasks;
using GameFramework.Core;
using GameFramework.Event;
using GameFramework.Network;
using GameFramework.Network.MessagePack;
using GameFramework.Network.Rpc;
using GameFramework.Reference;
using GameFramework.Request;
using GameFramework.Rpc;

namespace GameFramework.GameServer
{
    /// <summary>
    /// 游戏服务器交互服务实现
    /// </summary>
    internal sealed class GameServerServiceImpl : IGameServerService, IInitialize, IDisposable
    {
        [Inject]
        public INetworkService NetworkService { get; set; }
        
        [Inject]
        public IReferenceService ReferenceService { get; set; }
        
        [Inject]
        public IMessageFactory MessageFactory { get; set; }
        
        [Inject]
        public IEventService EventService { get; set; }
        
        [Inject]
        public IRpcService RpcService { get; set; }
        
        private const string GameServerChannelName = "GameServer";

        public bool Connected => m_GameServerChannel.Connected;
        
        private INetworkChannel m_GameServerChannel;
        private IPromise<int> m_ConnectPromise;
        private readonly HashSet<uint> m_WaitingRpcIds = new HashSet<uint>();
        private readonly HashSet<uint> m_ForEachBuffer = new HashSet<uint>();

        public void Initialize()
        {
            var helper = new MessagePackNetworkChannelHelper(MessageFactory, ReferenceService, RpcService);
            m_GameServerChannel = NetworkService.CreateNetworkChannel(GameServerChannelName, ServiceType.Tcp, helper);

            EventService.Subscribe<NetworkConnectedEventArgs>(OnNetworkConnected);
            EventService.Subscribe<NetworkClosedEventArgs>(OnNetworkClosed);
            EventService.Subscribe<NetworkErrorEventArgs>(OnNetworkError);
        }

        public void Dispose()
        {
            EventService.Unsubscribe<NetworkConnectedEventArgs>(OnNetworkConnected);
            EventService.Unsubscribe<NetworkClosedEventArgs>(OnNetworkClosed);
            EventService.Unsubscribe<NetworkErrorEventArgs>(OnNetworkError);
            
            NetworkService.DestroyNetworkChannel(GameServerChannelName);
        }

        public async UniTask<int> Connect(IPAddress ipAddress, int port)
        {
            if (Connected)
            {
                Disconnect();

                await UniTask.Yield(); // 断开事件会延迟一帧触发 这里需要等待一帧
            }

            var promise = AutoResetUniTaskCompletionSource<int>.Create();
            m_ConnectPromise = promise;
            
            var task = promise.Task;
            
            try
            {
                m_GameServerChannel.Connect(ipAddress, port);
            }
            catch (Exception)
            {
                TrySetConnectResult((int)GameFrameworkErrorCode.RequestCatchException);
            }
            
            return await task;
        }

        public void Disconnect()
        {
            m_GameServerChannel.Close();
        }

        public UniTask<IResponse> SendAsync(Packet packet)
        {
            // ReSharper disable once SuspiciousTypeConversion.Global
            return packet is IRpcPacket ? SendRpcPacket(packet) : SendSimplePacket(packet);
        }

        #region [网络事件]

        private void OnNetworkConnected(object sender, EventArgs e)
        {
            var eventArgs = (NetworkConnectedEventArgs) e;
            if (eventArgs.NetworkChannel != m_GameServerChannel)
                return;

            TrySetConnectResult((int)GameFrameworkErrorCode.Success);
        }

        private void OnNetworkClosed(object sender, EventArgs e)
        {
            var eventArgs = (NetworkClosedEventArgs) e;
            if (eventArgs.NetworkChannel != m_GameServerChannel)
                return;
            
            TrySetConnectResult((int)GameFrameworkErrorCode.GameServerManualDisconnect);
            SetErrorCodeForWaitingRpcIds((int)GameFrameworkErrorCode.GameServerDisconnect);
            EventService.Raise<GameServerDisconnectedEventArgs>(this);
        }

        private void OnNetworkError(object sender, EventArgs e)
        {
            var eventArgs = (NetworkErrorEventArgs) e;
            if (eventArgs.NetworkChannel != m_GameServerChannel)
                return;
            
            Log.Error(eventArgs.ErrorMessage);
            
            TrySetConnectResult(eventArgs.ErrorCode);
            Disconnect();
        }

        #endregion
        
        private void TrySetConnectResult(int errorCode)
        {
            if (m_ConnectPromise == null)
                return;

            var promise = m_ConnectPromise;
            m_ConnectPromise = null;
            promise.TrySetResult(errorCode);
        }
        
        /// <summary>
        /// 设置等待中的RpcId的错误码
        /// </summary>
        private void SetErrorCodeForWaitingRpcIds(int errorCode)
        {
            if (m_WaitingRpcIds.Count == 0)
                return;
            
            m_ForEachBuffer.Clear();
            m_ForEachBuffer.UnionWith(m_WaitingRpcIds);
            m_WaitingRpcIds.Clear();
            
            foreach (var rpcId in m_ForEachBuffer)
                RpcService.SetErrorCode(rpcId, errorCode);
        }

        /// <summary>
        /// 发送简单的协议包
        /// </summary>
        private UniTask<IResponse> SendSimplePacket(Packet packet)
        {
            try
            {
                m_GameServerChannel.Send(packet);
            
                // 非RPC请求的协议 在其进入发送队列后就当作成功处理
                var response = ReferenceService.GetReference<CommonResponse>();
                response.SetErrorCode((int)GameFrameworkErrorCode.Success);
                return UniTask.FromResult<IResponse>(response);
            }
            catch (Exception)
            {
                var response = ReferenceService.GetReference<CommonResponse>();
                response.SetErrorCode((int)GameFrameworkErrorCode.RequestCatchException);
                return UniTask.FromResult<IResponse>(response);
            }
        }

        /// <summary>
        /// 发送RPC协议包
        /// </summary>
        private async UniTask<IResponse> SendRpcPacket(Packet packet)
        {
            var rpcTask = RpcService.Allocate(CancellationToken.None);
            
            var rpcPacket = (IRpcPacket) packet;
            rpcPacket.RpcId = rpcTask.RpcId;
            
            try
            {
                m_GameServerChannel.Send(packet);
                
                // 进入发送队列成功 等待响应
                m_WaitingRpcIds.Add(rpcTask.RpcId);
            }
            catch (Exception)
            {
                // 进入发送队列失败 设置错误码
                RpcService.SetErrorCode(rpcTask.RpcId, (int)GameFrameworkErrorCode.RequestCatchException);
            }
                
            var response = await rpcTask.Task;
            m_WaitingRpcIds.Remove(rpcTask.RpcId);
            return response;
        }
    }
}